package multidraw.tools;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Properties;
import java.util.Vector;

import javax.swing.ImageIcon;

import multidraw.DrawingCanvasView;
import multidraw.model.DrawingCanvasModel;
import multidraw.util.Utils;

public class DynamicToolLoader {
	//all the tool that this user has
	protected ArrayList<ToolInfo> localToolList = new ArrayList<ToolInfo>();
	
	public String loaderID; //this is just a trick to work around the test on the same machine
	
	public ArrayList<ClassLoader> myClassLoaders = new ArrayList<ClassLoader>();
	
	public DynamicToolLoader() {
		//scan the tools folder and findout dynamic tools
		SecureRandom rand = new SecureRandom();
		byte[] buf = new byte[4];
		rand.nextBytes(buf);
		loaderID = Utils.getHex(buf);
	}
	
	public Vector<Object> buildDynamicToolList(DrawingCanvasModel model, DrawingCanvasView view) {
		Vector<Object> toolList = new Vector<Object>(5, 1);
		
		File dir = new File("tools");

		String[] children = dir.list();
		
		if (children == null) return toolList;
		
		for (String file: children) {
			System.out.println(file);
			File toolDir = new File(dir, file);
			
			//Now read the properties file
			// Read properties file.
			Properties properties = new Properties();
			try {
				File toolInfo = new File(toolDir, "tool-info.properties");
			    properties.load(new FileInputStream(toolInfo));
			    
			    String toolName = properties.getProperty("name");
			    File imgFile = new File(toolDir, properties.getProperty("image"));
			    System.out.println(properties.getProperty("factoryclass"));
			    String factoryClassName = properties.getProperty("factoryclass");
			    System.out.println(properties.getProperty("jar-file"));
			    File jarFile = new File(toolDir, properties.getProperty("jar-file"));
			    try {
			    	Class factoryCls = loadClass(jarFile, factoryClassName);
			    	DynamicToolFactory factory = (DynamicToolFactory) factoryCls.newInstance();
			    	this.myClassLoaders.add(factory.getClass().getClassLoader());
					Tool tool = factory.createTool(model, view);
					System.out.println("loaded tool: " + tool);
					toolList.add(new ToolController(toolName, getImageIcon(imgFile.getAbsolutePath()), 
							toolName, model, tool));
					ToolInfo info = new ToolInfo();
					info.toolName = toolName;
					info.version = properties.getProperty("version");
					localToolList.add(info);
			    } catch (Exception e) {
			    	System.out.println(e);
			    }
			} catch (IOException e) {
				System.out.println(e);
			}						
		}
		return toolList;
	}
	
	public static Class loadClass(File jarFile, String clsName) throws IOException, ClassNotFoundException {
		File file = jarFile;
		
		
		URLClassLoader clazzLoader = URLClassLoader.newInstance(new URL[]{file.toURI().toURL()});
//		System.class.getClassLoader()
		
		Class c = clazzLoader.loadClass(clsName);
		
		return c;
	}
	
	/**
	 * Attempts to load an Image from the local disk.
	 * 
	 * Retrieving the current working directory varies whether the program was
	 * executed as an Applet or an Application. Assumes that the images will be
	 * located at the top directory of the binary files.
	 * 
	 * @param fileName
	 *            file name of the image relative to the current working
	 *            directory
	 * @return new ImageIcon
	 */
	protected ImageIcon getImageIcon(String fileName) {
		return new ImageIcon(fileName);
	}// end protected ImageIcon getImageIcon( String )
	  
	
	
	public ArrayList<ToolInfo> getDynamicToolList() {
		return localToolList;
	}
	
	public Vector<Object> reloadSessionToolList(DrawingCanvasModel model, DrawingCanvasView view) {
		Vector<Object> toolList = new Vector<Object>(5, 1);
		
		if (!model.getLoggedInStatus()) {
			//just clean the session
			return toolList;
		}
		
		File dir = new File(new File("sessions", model.getSessionID()), loaderID);

		String[] children = dir.list();
		
		if (children == null) return toolList;
		
		for (String file: children) {
			System.out.println(file);
			File toolDir = new File(dir, file);
			
			//Now read the properties file
			// Read properties file.
			Properties properties = new Properties();
			try {
				File toolInfo = new File(toolDir, "tool-info.properties");
			    properties.load(new FileInputStream(toolInfo));
			    
			    String toolName = properties.getProperty("name");
			    File imgFile = new File(toolDir, properties.getProperty("image"));
			    System.out.println(properties.getProperty("factoryclass"));
			    String factoryClassName = properties.getProperty("factoryclass");
			    System.out.println(properties.getProperty("jar-file"));
			    File jarFile = new File(toolDir, properties.getProperty("jar-file"));
			    try {
			    	Class factoryCls = loadClass(jarFile, factoryClassName);
			    	this.myClassLoaders.add(factoryCls.getClassLoader());
			    	DynamicToolFactory factory = (DynamicToolFactory) factoryCls.newInstance();
					Tool tool = factory.createTool(model, view);
					System.out.println("loaded tool: " + tool);
					toolList.add(new ToolController(toolName, getImageIcon(imgFile.getAbsolutePath()), 
							toolName, model, tool));
					ToolInfo info = new ToolInfo();
					info.toolName = toolName;
					info.version = properties.getProperty("version");
					localToolList.add(info);
			    } catch (Exception e) {
			    	System.out.println(e);
			    }
			} catch (IOException e) {
				System.out.println(e);
			}						
		}
		return toolList;
	}
	
	public Vector<Class> reloadSessionToolList(String sessionID) {
		Vector<Class> toolList = new Vector<Class>(5, 1);
		
		File dir = new File(new File("server-sessions", sessionID), loaderID);

		String[] children = dir.list();
		
		if (children == null) return toolList;
		
		for (String file: children) {
			System.out.println(file);
			File toolDir = new File(dir, file);
			
			//Now read the properties file
			// Read properties file.
			Properties properties = new Properties();
			try {
				File toolInfo = new File(toolDir, "tool-info.properties");
			    properties.load(new FileInputStream(toolInfo));
			    
			    String toolName = properties.getProperty("name");
			    File imgFile = new File(toolDir, properties.getProperty("image"));
			    System.out.println(properties.getProperty("factoryclass"));
			    String factoryClassName = properties.getProperty("factoryclass");
			    System.out.println(properties.getProperty("jar-file"));
			    File jarFile = new File(toolDir, properties.getProperty("jar-file"));
			    try {
			    	Class factoryCls = loadClass(jarFile, factoryClassName);
			    	this.myClassLoaders.add(factoryCls.getClassLoader());
			    	//DynamicToolFactory factory = (DynamicToolFactory) factoryCls.newInstance();
			    	toolList.add(factoryCls);
					
			    } catch (Exception e) {
			    	System.out.println(e);
			    }
			} catch (IOException e) {
				System.out.println(e);
			}						
		}
		return toolList;
	}
	
	public Vector<Object> addNewTool(File toolDir, DrawingCanvasModel model, DrawingCanvasView view) {
		Vector<Object> toolList = new Vector<Object>(5, 1);
		
		//copy the file into the tools folder.
		try {
			copyDirectory(toolDir, new File("tools", toolDir.getName()));
		} catch (IOException e) {
			System.out.println(e);
		}
		
		//System.out.println(file);
		//File toolDir = new File(dir, file);
		
		//Now read the properties file
		// Read properties file.
		Properties properties = new Properties();
		try {
			File toolInfo = new File(toolDir, "tool-info.properties");
		    properties.load(new FileInputStream(toolInfo));
		    
		    String toolName = properties.getProperty("name");
		    File imgFile = new File(toolDir, properties.getProperty("image"));
		    System.out.println(properties.getProperty("factoryclass"));
		    String factoryClassName = properties.getProperty("factoryclass");
		    System.out.println(properties.getProperty("jar-file"));
		    File jarFile = new File(toolDir, properties.getProperty("jar-file"));
		    try {
		    	Class factoryCls = loadClass(jarFile, factoryClassName);
		    	DynamicToolFactory factory = (DynamicToolFactory) factoryCls.newInstance();
		    	this.myClassLoaders.add(factory.getClass().getClassLoader());
				Tool tool = factory.createTool(model, view);
				System.out.println("loaded tool: " + tool);
				toolList.add(new ToolController(toolName, getImageIcon(imgFile.getAbsolutePath()), 
						toolName, model, tool));
				ToolInfo info = new ToolInfo();
				info.toolName = toolName;
				info.version = properties.getProperty("version");
				localToolList.add(info);
		    } catch (Exception e) {
		    	System.out.println(e);
		    }
		} catch (IOException e) {
			System.out.println(e);
		}	
		return toolList;
	}
	
	 // If targetLocation does not exist, it will be created.
	public void copyDirectory(File sourceLocation, File targetLocation)
			throws IOException {

		if (sourceLocation.isDirectory()) {
			if (!targetLocation.exists()) {
				targetLocation.mkdir();
			}

			String[] children = sourceLocation.list();
			for (int i = 0; i < children.length; i++) {
				copyDirectory(new File(sourceLocation, children[i]), new File(
						targetLocation, children[i]));
			}
		} else {

			InputStream in = new FileInputStream(sourceLocation);
			OutputStream out = new FileOutputStream(targetLocation);

			// Copy the bits from instream to outstream
			byte[] buf = new byte[1024];
			int len;
			while ((len = in.read(buf)) > 0) {
				out.write(buf, 0, len);
			}
			in.close();
			out.close();
		}
	}
}
